# Copyright (C) 2020-2024 Intel Corporation
# SPDX-License-Identifier: MIT

cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

if(MSVC AND (MSVC_VERSION LESS 1900))
message(FATAL_ERROR "Visual Studio Compiler Version >= 1900 Required to build.")
endif()

# This project follows semantic versioning (https://semver.org/)
project(level-zero VERSION 1.18.3)

include(GNUInstallDirs)

find_package(Git)
if(Git_FOUND)
	if(MSVC)
		execute_process(
			COMMAND CMD /c ${GIT_EXECUTABLE} rev-parse HEAD
			OUTPUT_VARIABLE VERSION_SHA
			OUTPUT_STRIP_TRAILING_WHITESPACE
			WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
		)
	else()
	execute_process(
		COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
		OUTPUT_VARIABLE VERSION_SHA
		OUTPUT_STRIP_TRAILING_WHITESPACE
		WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
	)
	endif()
else()
	set(VERSION_SHA " - No git SHA found, compiled outside git folder.")
endif()
add_definitions(-DLOADER_VERSION_SHA="${VERSION_SHA}")

if(SYSTEM_SPDLOG)
	find_package(spdlog CONFIG)
	if(spdlog_FOUND)
		message(STATUS "System spdlog found.")
	else()
		message(FATAL_ERROR "SYSTEM_SPDLOG specified but spdlog wasn't found.")
	endif()
else()
    include_directories("${CMAKE_CURRENT_SOURCE_DIR}/third_party/spdlog_headers")
endif()

include(FetchContent)

if(BUILD_L0_LOADER_TESTS)
	FetchContent_Declare(
		googletest
		URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip
	)

	# For Windows: Prevent overriding the parent project's compiler/linker settings
	set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)

	FetchContent_MakeAvailable(googletest)

	enable_testing()
endif()

# Update other relevant variables to include the patch
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
set(CMAKE_PROJECT_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
set(CMAKE_PROJECT_VERSION "${PROJECT_VERSION}")
add_definitions(-DLOADER_VERSION_MAJOR=${PROJECT_VERSION_MAJOR})
add_definitions(-DLOADER_VERSION_MINOR=${PROJECT_VERSION_MINOR})
add_definitions(-DLOADER_VERSION_PATCH=${PROJECT_VERSION_PATCH})

file(WRITE "${CMAKE_BINARY_DIR}/VERSION" "${PROJECT_VERSION}")

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

#Define a path for custom commands to work around MSVC
set(CUSTOM_COMMAND_BINARY_DIR ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
if(MSVC)
    #MSVC implicitly adds $<CONFIG> to the output path
    set(CUSTOM_COMMAND_BINARY_DIR ${CUSTOM_COMMAND_BINARY_DIR}/$<CONFIG>)
    #enabling Control Flow Guard
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /guard:cf")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /guard:cf")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DYNAMICBASE")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /DYNAMICBASE")
    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${CMAKE_CXX_LINKER_WRAPPER_FLAG}/DYNAMICBASE")
    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${CMAKE_CXX_LINKER_WRAPPER_FLAG}/guard:cf")
    # enable Spectre Mitigation, not supported by clang-cl
    if((NOT CMAKE_CXX_COMPILER_ID STREQUAL Clang) AND (NOT CMAKE_CXX_COMPILER_ID STREQUAL IntelLLVM))
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Qspectre")
    endif()
    if((NOT CMAKE_C_COMPILER_ID STREQUAL Clang) AND NOT (CMAKE_C_COMPILER_ID STREQUAL IntelLLVM))
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Qspectre")
    endif()
endif()

#CXX compiler support
if(NOT MSVC)
    include(CheckCXXCompilerFlag)
    CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14)
    if(COMPILER_SUPPORTS_CXX14)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
    else()
        message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++14 support.  Please use a different C++ compiler.")
    endif()
    if (UNIX)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -fPIC")
    else()
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive")
    endif()
    if(NOT CMAKE_CXX_COMPILER_ID STREQUAL IntelLLVM)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Wnon-virtual-dtor")
    endif()
endif()

#MSVC compile flags
if(MSVC)
    string(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
    string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")

    if(NOT CMAKE_CXX_COMPILER_ID STREQUAL IntelLLVM)
        # treat warnings as errors
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /WX /W3")
    endif()

    # enable multi-process compilation, not supported by clang-cl
    if((NOT CMAKE_CXX_COMPILER_ID STREQUAL Clang) AND (NOT CMAKE_CXX_COMPILER_ID STREQUAL IntelLLVM))
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
    endif()

    # enable exceptions handling
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc")

    # enable creation of PDB files for Release Builds
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi")
    set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${CMAKE_CXX_LINKER_WRAPPER_FLAG}/DEBUG ${CMAKE_CXX_LINKER_WRAPPER_FLAG}/OPT:REF ${CMAKE_CXX_LINKER_WRAPPER_FLAG}/OPT:ICF")

    # enable CET shadow stack
    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${CMAKE_CXX_LINKER_WRAPPER_FLAG}/CETCOMPAT")

    #Use of sccache with MSVC requires workaround of replacing /Zi with /Z7
    #https://github.com/mozilla/sccache
    if(USE_Z7) #sccache
      string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
      string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
      string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
      string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
      string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
      string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
    endif()

endif()

if(USE_ASAN)
    if(NOT MSVC)
        # -fno-omit-frame-pointer is included for nicer stack traces in error messages
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
        set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
        set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address")
    else()
        message(WARNING "Address Sanitizer is not supported on Windows")
    endif()
endif()

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/source/wrapper/include)


include_directories("${CMAKE_CURRENT_SOURCE_DIR}")

set(TARGET_LOADER_NAME ze_loader)


add_subdirectory(source)
add_subdirectory(samples)

if(BUILD_L0_LOADER_TESTS)
    include(CTest)
    add_subdirectory(test)
endif()

include("os_release_info.cmake")
get_os_release_info(os_name os_version os_codename)
string(APPEND os_type "${os_name}")
string(COMPARE EQUAL "${os_type}" "sles" sles_distro)
#Pick only first character of os_name
string(SUBSTRING "${os_name}" 0 1 os_name)

file(GLOB LEVEL_ZERO_API_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h")

install(FILES ${LEVEL_ZERO_API_HEADERS}
    DESTINATION ./include/level_zero
    COMPONENT level-zero-devel
)

file(GLOB LEVEL_ZERO_LAYERS_API_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/include/layers/*.h")

install(FILES ${LEVEL_ZERO_LAYERS_API_HEADERS}
    DESTINATION ./include/level_zero/layers
    COMPONENT level-zero-devel
)

file(GLOB LEVEL_ZERO_LOADER_API_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/include/loader/*.h")

install(FILES ${LEVEL_ZERO_LOADER_API_HEADERS}
    DESTINATION ./include/level_zero/loader
    COMPONENT level-zero-devel
)

# If generators list was not define build native package for current distro
if(NOT DEFINED CPACK_GENERATOR)
	if(EXISTS "/etc/debian_version")
		set(CPACK_GENERATOR "DEB")
	elseif(EXISTS "/etc/redhat-release")
		set(CPACK_GENERATOR "RPM")
	elseif(EXISTS "/etc/SUSE-brand" OR EXISTS "/etc/SUSE-release" OR sles_distro)
		set(CPACK_GENERATOR "RPM")
	else()
		set(CPACK_GENERATOR "ZIP")
	endif()
endif()

if(MSVC)
	set(CPACK_SET_DESTDIR FALSE)
	set(CPACK_PACKAGING_INSTALL_PREFIX "")
else()
	set(CPACK_SET_DESTDIR TRUE)
endif()
set(CPACK_PACKAGE_RELOCATABLE FALSE)
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "oneAPI Level Zero")
set(CPACK_PACKAGE_VENDOR "Intel")

set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CMAKE_INSTALL_PREFIX})
set(CPACK_PACKAGE_CONTACT "Intel Corporation")

set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")

if(CPACK_GENERATOR MATCHES "RPM")
	set(CPACK_RPM_COMPRESSION_TYPE "xz")
	string(FIND "${CMAKE_CXX_COMPILER}" "aarch64" compiler_arch_check)
	if((NOT ${compiler_arch_check} MATCHES "-1") OR (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64"))
		set(CPACK_RPM_PACKAGE_ARCHITECTURE "aarch64")
	elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64")
		set(CPACK_RPM_PACKAGE_ARCHITECTURE "x86_64")
	endif()
	set(CPACK_RPM_PACKAGE_AUTOREQ OFF)
	set(CPACK_RPM_PACKAGE_DESCRIPTION "oneAPI Level Zero")
	set(CPACK_RPM_PACKAGE_GROUP "System Environment/Libraries")
	set(CPACK_RPM_PACKAGE_LICENSE "MIT")
	set(CPACK_RPM_PACKAGE_RELEASE 1)
	set(CPACK_RPM_PACKAGE_RELEASE_DIST ON)
	set(CPACK_RPM_PACKAGE_URL "https://github.com/oneapi-src/level-zero")
	set(CPACK_RPM_COMPONENT_INSTALL ON)
	set(CPACK_RPM_LEVEL-ZERO_PACKAGE_NAME "${PROJECT_NAME}")
	set(CPACK_RPM_LEVEL-ZERO-DEVEL_PACKAGE_NAME "${PROJECT_NAME}-devel")
	set(CPACK_RPM_LEVEL-ZERO_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-${os_name}${os_version}.${CPACK_RPM_PACKAGE_ARCHITECTURE}.rpm")
	set(CPACK_RPM_LEVEL-ZERO-DEVEL_FILE_NAME "${PROJECT_NAME}-devel-${PROJECT_VERSION}-${os_name}${os_version}.${CPACK_RPM_PACKAGE_ARCHITECTURE}.rpm")
	set(CPACK_RPM_LEVEL-ZERO-DEVEL_PACKAGE_REQUIRES "level-zero = ${PROJECT_VERSION}")


	set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION
	  /etc/ld.so.conf.d
	  /usr/local
	  /usr/local/lib64
	  /usr/local/bin
	  /usr/local/include
	)
endif()

if(CPACK_GENERATOR MATCHES "DEB")
	string(FIND "${CMAKE_CXX_COMPILER}" "aarch64" compiler_arch_check)
	if((NOT ${compiler_arch_check} MATCHES "-1") OR (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64"))
		SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE arm64)
	elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64")
		SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE amd64)
	endif()
	set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/oneapi-src/level-zero")
	set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
	set(CPACK_DEBIAN_LEVEL-ZERO_PACKAGE_NAME "${PROJECT_NAME}")
	set(CPACK_DEBIAN_LEVEL-ZERO-DEVEL_PACKAGE_NAME "${PROJECT_NAME}-devel")
    set(CPACK_DEBIAN_LEVEL-ZERO_FILE_NAME "${PROJECT_NAME}_${PROJECT_VERSION}+${os_name}${os_version}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb")
    set(CPACK_DEBIAN_LEVEL-ZERO-DEVEL_FILE_NAME "${PROJECT_NAME}-devel_${PROJECT_VERSION}+${os_name}${os_version}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb")
    set(CPACK_DEBIAN_LEVEL-ZERO-DEVEL_PACKAGE_DEPENDS "level-zero(=${PROJECT_VERSION})")
    set(CPACK_DEB_COMPONENT_INSTALL ON)
    set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
endif()

INCLUDE(CPack)
